package com.heima.search.service;

import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.JsonUtils;
import com.heima.feign.ApArticleFeignClient;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.media.dtos.WmNewsDownOrUpDTO;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.pojo.ArticleDoc;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class ApUserSearchService {

    public static final String INDEX_NAME = "app_info_article";

    @Autowired
    RestHighLevelClient client;

    @Autowired
    private ApArticleFeignClient articleFeignClient;

    @Autowired
    private SearchHistoryService historyService;

    public void importArticle(List<ArticleDto> articleDtoList) {
        BulkRequest bulkRequest = new BulkRequest();
        for(ArticleDto dto : articleDtoList){
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME).id(""+dto.getId());
            ArticleDoc doc = new ArticleDoc(dto);
            indexRequest.source(JsonUtils.toString(doc), XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        try {
            client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("Article数据批量导入ES异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }

    }

    /**
     * article文章创建以后的回调
     * */
    public void onArticleCreate(long articleId) {
        // articleId查询article
        ArticleDto articleDto = articleFeignClient.findById(articleId);
        if(articleDto == null){
            log.error("根据articleId查询不到article信息:{}", articleId);
            return;
        }
        // 构造articleDoc
        ArticleDoc articleDoc = new ArticleDoc(articleDto);
        // 写入到ES
        IndexRequest indexRequest = new IndexRequest(INDEX_NAME).id("" + articleId);
        indexRequest.source(JsonUtils.toString(articleDoc), XContentType.JSON);
        try {
            client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("创建文档内容异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    public void downOrUp(WmNewsDownOrUpDTO dto) {
        // 如果是上架，则insert
        if(dto.getEnable()){
            onArticleCreate(dto.getArticleId());
        }else{
            // 如果是下架，就从es删除
            DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, ""+dto.getArticleId());
            try {
                client.delete(deleteRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                log.error("删除文档异常:{}", ExceptionUtils.getStackTrace(e));
                throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
            }
        }
    }

    public List<ArticleDoc> search(UserSearchDto dto) {
        SearchRequest seachRequest = new SearchRequest(INDEX_NAME);
        // 查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(dto.getSearchWords(), "title","content"));
        if(dto.getMinBehotTime() != null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        }
        seachRequest.source().query(boolQueryBuilder);
        //分页
        seachRequest.source().from(0);
        seachRequest.source().size(dto.getPageSize());
        // 排序
        seachRequest.source().sort("publishTime", SortOrder.DESC);
        // 返回除了content之外的所有字段
        seachRequest.source().fetchSource(null, "content");
        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'>").postTags("</font>").field("title").requireFieldMatch(false);
        seachRequest.source().highlighter(highlightBuilder);
        // 发请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(seachRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("搜索异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
        // 解析响应
        SearchHits hits = searchResponse.getHits();
        List<ArticleDoc> result  = new ArrayList<>(10);
        for(SearchHit hit : hits.getHits()){
            String json = hit.getSourceAsString();
            ArticleDoc articleDoc = JsonUtils.toBean(json, ArticleDoc.class);
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            // 处理高亮
            if(!CollectionUtils.isEmpty(highlightFields)){
                HighlightField highlightField = highlightFields.get("title");
                if(highlightField != null){
                    Text[] fragments = highlightField.getFragments();
                    if(fragments != null && fragments.length > 0){
                        String title = fragments[0].toString();
                        articleDoc.setTitle(title);
                    }
                }
            }
            result.add(articleDoc);
        }
        // 保存搜索的历史记录
        historyService.saveSearchHistory(dto.getSearchWords(), UserThreadLocalUtils.getUserId(), dto.getEquipmentId());
        return result;
    }

}
